import math
import numpy as np
from matplotlib import pyplot as plt

from common_function import Michalewicz
import do_pic


class Sparrow:
    # ##初始化变量
    # dim=10#维度
    # pq=100#总群数量
    # iter_num=100#迭代次数
    # ST=0.1#发现者警戒阈值
    # PR=0.2#发现者比例
    # SD=0.15#侦察者比例
    # data_range_max=1#取值范围
    # data_range_min=0#取值范围
    # ty=## 最佳函数是最大还是最小值
    E = 1  # 最小常数

    def __init__(self, d, p, i, S_T, P, S_D, d_min, d_max, n_t):
        self.dim = d  # 维度
        self.pq = p  # 总群数量
        self.iter_num = i  # 迭代次数
        self.ST = S_T  # 发现者警戒阈值
        self.PR = P  # 发现者比例
        self.SD = S_D  # 侦察者比例
        self.data_range_max = d_max  # 取值范围
        self.data_range_min = d_min  # 取值范围
        self.ty = n_t  # 最佳函数是最大还是最小值
        self.num_PR = int(self.pq * self.PR)  # 发现者麻雀群落中单体个数
        self.num_FL = int(self.pq * (1 - self.PR - self.SD))  # 跟随者麻雀群落中单体个数
        self.num_SD = int(self.pq * self.SD)  # 侦查者麻雀群落中单体个数
        """
                 创建群落
                 SSA_ST：发现者群落
                 SSA_FL：跟随者群落
                 SSA_SD：侦察者群落
                 """
        self.SSA_ST = self.create_PR_community()
        self.SSA_FL = self.create_Fl_community()
        self.SSA_SD = self.create_SD_community()
        self.best_x = []
        self.best_function = 0
        self.worst_x = []
        self.worst_function = 0

    def create_PR_community(self):  # 创造发现者群落
        return np.random.uniform(self.data_range_min, self.data_range_max, (self.num_PR, self.dim))

    def create_Fl_community(self):  # 创造跟随者群落
        return np.random.uniform(self.data_range_min, self.data_range_max, (self.num_FL, self.dim))

    def create_SD_community(self):  # 创造侦察者群落
        return np.random.uniform(self.data_range_min, self.data_range_max, (self.num_SD, self.dim))

    def Predator_position(self, local, Xid_t):  # 发现者麻雀：第local只的位置更新
        """
        :param local: 第local只麻雀
        :param Xid_t: 这只麻雀所在的位置
        :return: Xid_t1 更新这只麻雀所在的位置
        """
        local = local + 1
        round_01 = np.random.rand(1)
        if np.random.rand() < self.ST:
            Xid_t1 = Xid_t * math.exp((-local) / (round_01 * self.iter_num))
        else:
            Xid_t1 = Xid_t + np.random.randn() * np.ones(self.dim)
        return Xid_t1

    def Subscriber_position(self, local, Xij_t, X_best, X_worst):  # 跟随着麻雀：第local只的位置更新
        """
        :param local: 第local只麻雀
        :param Xij_t: 这只麻雀所在的位置
        :param X_best: 全局最好的位置
        :param X_worst:全局最差的位置
        :return: Xij_t1：更新这只麻雀所在的位置
        """
        local = local + 1
        A = np.random.uniform(-1.0, 1.0, self.dim)
        AT = np.matrix(A).T
        A = AT.T
        a = np.dot(A, AT)
        A_jia = np.multiply((1 / a[0]), AT)
        if local > (self.pq / 2):

            sub = np.matrix((X_worst - Xij_t))

            Xij_t1 = np.random.randn() * np.multiply(1 / (local ** 2), np.exp((X_worst - Xij_t)))
        else:
            absx = np.matrix(abs(Xij_t - X_best))
            lin = np.dot(absx, A_jia)
            Xij_t1 = X_best + np.multiply(lin, (np.ones(self.dim).T))

        return Xij_t1

    def Lookout_position(self, local, Xij_t, X_best, X_worst, fi, fg, fw):
        """
        :param local: 第local只麻雀
        :param Xij_t: 这只麻雀所在的位置
        :param X_best:全局最好的位置
        :param X_worst:全局最差的位置s
        :param fi:当前麻雀的自适应度
        :param fg:全局最佳的适应度值
        :param fw:全局最差的适应度值
        :return:Xij_t1 更新麻雀所在的位置
        """
        B = np.random.randn()
        K = np.random.uniform(-1.0, 1.0)
        print(abs(Xij_t - X_worst))
        if fi > fg and self.ty==1:
            Xij_t1 = X_best + B * abs(Xij_t - X_best)
        elif fi ==fg:
            Xij_t1 = Xij_t + K *(abs(Xij_t - X_worst) / ((fi - fw) + self.E))
        elif fi < fg and self.ty==0:
            Xij_t1 = X_best + B * abs(Xij_t - X_best)
        else:Xij_t1=Xij_t
        return Xij_t1

    def adaptation_function(self):
        """
        选择Michalewicz函数
        求解每只麻雀的适应度
        :return:fx_ST, fx_FL, fx_SD：分别代表发现者，跟锁者，侦察者的适应度
        """
        fx_ST = []
        fx_FL = []
        fx_SD = []
        for i in range(len(self.SSA_ST)):
            fx_ST.append(Michalewicz.Michalewicz(self.dim, self.SSA_ST[i], 10))
        for n in range(len(self.SSA_FL)):
            fx_FL.append(Michalewicz.Michalewicz(self.dim, self.SSA_FL[n], 10))
        for m in range(len(self.SSA_SD)):
            fx_SD.append(Michalewicz.Michalewicz(self.dim, self.SSA_SD[m], 10))
        return fx_ST, fx_FL, fx_SD

    def request_best_min(self, fx_ST, fx_FL, fx_SD, SSA_ST, SSA_FL, SSA_SD):  # 以最小值为优
        """
        目标函数
        在全局群落中的最优解（最小值）以及提供最优解的麻雀位置
        在全局群落中的最差解（最大值）以及提供最差解的麻雀位置
        :param fx_ST: 发现者群里的适应度
        :param fx_FL: 跟随着的适应度
        :param fx_SD: 警惕着的适应度
        :param SSA_ST: 发现者的位置
        :param SSA_FL: 跟随者的位置
        :param SSA_SD: 警惕着的位置
        :return: x_best：提供最优解的麻雀位置,
                 x_worst：提供最差解的麻雀位置
                 fx_best：最优解
                 fx_worst：最差解
        """
        nums_worst = []
        max_fx_ST = max(fx_ST)
        maxst_index = fx_ST.index(max_fx_ST)
        max_fx_FL = max(fx_FL)
        maxfl_index = fx_FL.index(max_fx_FL)
        max_fx_SD = max(fx_SD)
        maxsd_index = fx_SD.index(max_fx_SD)
        nums_worst.append(max_fx_ST)
        nums_worst.append(max_fx_FL)
        nums_worst.append(max_fx_SD)
        fx_worst = max(nums_worst)
        if nums_worst.index(fx_worst) == 0:
            x_worst = SSA_ST[maxst_index]
        elif nums_worst.index(fx_worst) == 1:
            x_worst = SSA_FL[maxfl_index]
        else:
            x_worst = SSA_SD[maxsd_index]

        nums_best = []
        min_fx_ST = min(fx_ST)
        minst_index = fx_ST.index(min_fx_ST)
        min_fx_FL = min(fx_FL)
        minfl_index = fx_FL.index(min_fx_FL)
        min_fx_SD = min(fx_SD)
        minsd_index = fx_SD.index(min_fx_SD)
        nums_best.append(min_fx_ST)
        nums_best.append(min_fx_FL)
        nums_best.append(min_fx_SD)
        fx_best = min(nums_best)
        if nums_best.index(fx_best) == 0:
            x_best = SSA_ST[minst_index]
        elif nums_best.index(fx_best) == 1:
            x_best = SSA_FL[minfl_index]
        else:
            x_best = SSA_SD[minsd_index]

        return x_best, x_worst, fx_best, fx_worst



    def request_best_max(self, fx_ST, fx_FL, fx_SD, SSA_ST, SSA_FL, SSA_SD):  # 以最大值为优
        """
               目标函数
               在全局群落中的最优解（最大值）以及提供最优解的麻雀位置
               在全局群落中的最差解（最小值）以及提供最差解的麻雀位置
               :param fx_ST: 发现者群里的适应度
               :param fx_FL: 跟随着的适应度
               :param fx_SD: 警惕着的适应度
               :param SSA_ST: 发现者的位置
               :param SSA_FL: 跟随者的位置
               :param SSA_SD: 警惕着的位置
               :return: x_best：提供最优解的麻雀位置`,
                        x_worst：提供最差解的麻雀位置
                        fx_best：最优解
                        fx_worst：最差解
               """
        nums_best = []
        max_fx_ST = max(fx_ST)
        maxst_index = fx_ST.index(max_fx_ST)
        max_fx_FL = max(fx_FL)
        maxfl_index = fx_FL.index(max_fx_FL)
        max_fx_SD = max(fx_SD)
        maxsd_index = fx_SD.index(max_fx_SD)
        nums_best.append(max_fx_ST)
        nums_best.append(max_fx_FL)
        nums_best.append(max_fx_SD)
        fx_best = max(nums_best)
        if nums_best.index(fx_best) == 0:
            x_best = SSA_ST[maxst_index]
        elif nums_best.index(fx_best) == 1:
            x_best = SSA_FL[maxfl_index]
        else:
            x_best = SSA_SD[maxsd_index]

        nums_worst = []
        min_fx_ST = min(fx_ST)
        minst_index = fx_ST.index(min_fx_ST)
        min_fx_FL = min(fx_FL)
        minfl_index = fx_FL.index(min_fx_FL)
        min_fx_SD = min(fx_SD)
        minsd_index = fx_SD.index(min_fx_SD)
        nums_worst.append(min_fx_ST)
        nums_worst.append(min_fx_FL)
        nums_worst.append(min_fx_SD)
        fx_worst = min(nums_worst)
        if nums_worst.index(fx_worst) == 0:
            x_worst = SSA_ST[minst_index]
        elif nums_worst.index(fx_worst) == 1:
            x_worst = SSA_FL[minfl_index]
        else:
            x_worst = SSA_SD[minsd_index]

        return x_best, x_worst, fx_best, fx_worst

    def SSA_course(self):
        """
        :return: x_best, fg 一次迭代后最优解与提供最优解麻雀的位置
        """

        for p in range(self.pq):
            fx_ST, fx_FL, fx_SD = self.adaptation_function()  # 获取适应值即每只麻雀的函数值

            if self.ty==1:
                x_best, x_worst, fg, fw = self.request_best_max(fx_ST, fx_FL, fx_SD, self.SSA_ST, self.SSA_FL, self.SSA_SD)
            else:x_best, x_worst, fg, fw = self.request_best_min(fx_ST, fx_FL, fx_SD, self.SSA_ST, self.SSA_FL, self.SSA_SD)

            if self.best_function == 0:
                self.best_function = fg
                self.worst_function = fw
                self.best_x = x_best
                self.worst_x = x_worst
            else:
                if self.ty == 1 and self.best_function < fg:
                    self.best_function = fg
                    self.best_x = x_best
                    if self.worst_function > fw:
                        self.worst_function = fw
                        self.worst_x = x_worst
                elif self.ty == 0 and self.best_function > fg:
                    self.best_function = fg
                    self.best_x = x_best
                    if self.worst_function < fw:
                        self.worst_function = fw
                        self.worst_x = x_worst
                else:
                    pass

            # 获取全局麻雀中最佳与最差适应值，以及提供该适应值的麻雀所在位置
            # 更新所有群落中麻雀位置

            do_pic.do_pic(self.SSA_ST)
            do_pic.do_pic(self.SSA_FL)
            do_pic.do_pic(self.SSA_SD)
            plt.draw()
            plt.pause(2)
            plt.clf()
            for i in range(len(self.SSA_ST)):  # 更新发现者群落中麻雀的位置
                ST_stime = self.Predator_position(i, self.SSA_ST[i])
                data_max = np.sum(ST_stime >= self.data_range_max)
                data_min = np.sum(ST_stime <= self.data_range_min)
                if data_max == 0 and data_min == 0:  # 限制范围
                    self.SSA_ST[i] = ST_stime
                else:
                    pass

            for n in range(len(self.SSA_FL)):  # 更新跟随者群落中麻雀的位置
                FL_stime = self.Subscriber_position(n, self.SSA_FL[n], self.best_x, self.worst_x)
                data_max = np.sum(FL_stime >= self.data_range_max)
                data_min = np.sum(FL_stime <= self.data_range_min)
                if data_max == 0 and data_min == 0:
                    self.SSA_FL[n] = FL_stime
                else:
                    pass

            for m in range(len(self.SSA_SD)):  # 更新侦察者群落中麻雀的位置
                fi = Michalewicz.Michalewicz(self.dim, self.SSA_SD[m], 10)
                SD_stime = self.Lookout_position(m, self.SSA_SD[m], self.best_x, self.worst_x, fi, self.best_function,
                                                 self.worst_function)
                data_max = np.sum(SD_stime >= self.data_range_max)
                data_min = np.sum(SD_stime <= self.data_range_min)
                if data_max == 0 and data_min == 0:
                    self.SSA_SD[m] = SD_stime
                else:
                    pass
        return x_best, fg
